package flatlandRPGTest;

import java.io.InvalidClassException;
import flatlandRPG.GLPolygon;
import java.util.ArrayList;
import java.io.IOException;
import java.io.File;
import flatlandRPG.MapLoader.DataFormatException;
import flatlandRPG.MapLoader;
import flatlandRPG.Building;
import flatlandRPG.Entity;
import flatlandRPG.GLRegularPolygon;
import flatlandRPG.Player;
import org.lwjgl.util.vector.Vector2f;
import student.*;

// -------------------------------------------------------------------------
/**
 * Tests the MapLoader class.
 *
 * @author evant
 * @version Apr 23, 2011
 */
public class MapLoaderTest
    extends TestCase
{
    private MapLoader testLoader;


    public void setUp()
    {
        testLoader = new MapLoader();
    }


    // ----------------------------------------------------------
    /**
     * Tests that loading a player, which has no constructor, works correctly.
     */
    public void testLoadPlayer()
    {
        Entity loadedEntity = load("Player\n0 1");

        assertNotNull(loadedEntity);
        assertTrue(loadedEntity instanceof Player);

        Vector2f location = loadedEntity.getLocation();
        assertEquals(0, location.getX(), 0.0001);
        assertEquals(1, location.getY(), 0.0001);
    }


    /**
     * Tests that negative numbers for location are parsed correctly.
     */
    public void testLoadNegativePosition()
    {
        Entity loadedEntity = load("Player\n-1 -2.5");

        assertNotNull(loadedEntity);
        assertTrue(loadedEntity instanceof Player);

        Vector2f location = loadedEntity.getLocation();
        assertEquals(-1, location.getX(), 0.0001);
        assertEquals(-2.5, location.getY(), 0.0001);
    }


    // ----------------------------------------------------------
    /**
     * Tests that loading a building, which has a constructor which is an array
     * of floats, works correctly.
     */
    public void testLoadBuilding()
    {
        Entity loadedEntity = load("Building\n0 0 0 1 1 1\n0 1");

        assertTrue(loadedEntity instanceof Building);

        Vector2f location = loadedEntity.getLocation();
        assertEquals(0, location.getX(), 0.0001);
        assertEquals(1, location.getY(), 0.0001);
    }


    // ----------------------------------------------------------
    /**
     * Tests that loading a regular polygon, which has a constructor which takes
     * a float and integer, works correctly.
     */
    public void testLoadRegularPolygon()
    {
        Entity loadedEntity = load("GLRegularPolygon\n0.5 4\n0 1");

        assertTrue(loadedEntity instanceof GLRegularPolygon);

        assertEquals(4, ((GLRegularPolygon)loadedEntity).getSides(), 0.0001);
        assertEquals(0.5f, ((GLRegularPolygon)loadedEntity).getRadius(), 0.0001);

        Vector2f location = loadedEntity.getLocation();
        assertEquals(0, location.getX(), 0.0001);
        assertEquals(1, location.getY(), 0.0001);
    }


    // ----------------------------------------------------------
    /**
     * Tests that loading a class that doesn't exist throws an exception.
     */
    public void testLoadInvalidClass()
    {
        testException("BadClass");
    }


    // ----------------------------------------------------------
    /**
     * Tests that loading a class with bad arguments throws an exception.
     */
    public void testLoadBadArguments()
    {
        testException("Building\n not a number");
    }


    // ----------------------------------------------------------
    /**
     * Tests loading entities from a file.
     */
    public void testLoadFile()
    {
        ArrayList<Entity> entities = new ArrayList<Entity>();

        try
        {
            entities = testLoader.load(new File("testMap.txt"));
        }
        catch (IOException e)
        {
            e.printStackTrace();
            fail(e.getMessage());
        }

        assertEquals(3, entities.size());

        assertTrue(entities.get(0) instanceof Player);
        Vector2f location = entities.get(0).getLocation();
        assertEquals(0.5, location.getX(), 0.0001);
        assertEquals(0.5, location.getY(), 0.0001);

        assertTrue(entities.get(1) instanceof Building);
        location = entities.get(1).getLocation();
        assertEquals(0, location.getX(), 0.0001);
        assertEquals(0, location.getY(), 0.0001);

        assertTrue(entities.get(2) instanceof GLPolygon);
        location = entities.get(2).getLocation();
        assertEquals(1, location.getX(), 0.0001);
        assertEquals(1, location.getY(), 0.0001);
    }


    // ----------------------------------------------------------
    /**
     * Tests that using a class with no valid constructor throws an exception.
     */
    public void testNoValidConstructor()
    {
        Exception occured = null;
        try
        {
            testLoader.load("Actor\n1 1\n0 0");
        }
        catch (Exception exception)
        {
            occured = exception;
        }
        assertNotNull(occured);
        assertTrue(occured instanceof DataFormatException);
        assertEquals("Unable to parse map file", occured.getMessage());

        assertTrue(occured.getCause() instanceof InvalidClassException);
        assertEquals("Class does not have valid constructor", occured
            .getCause().getMessage());
    }


    /**
     * A helper method to load from a string and catch any exceptions. This
     * should only be used for test with valid inputs.
     *
     * @param input
     *            the input string to load
     * @return the Entity loaded, or null if the entity failed to load
     */
    private Entity load(String input)
    {
        try
        {
            return testLoader.load(input);
        }
        catch (DataFormatException e)
        {
            fail(e.getMessage() + ": " + e.getCause().getMessage());
            return null;
        }
    }


    private void testException(String badInput)
    {
        Exception occured = null;
        try
        {
            testLoader.load(badInput);
        }
        catch (Exception exception)
        {
            occured = exception;
        }
        assertNotNull(occured);
        assertTrue(occured instanceof DataFormatException);
        assertEquals("Unable to parse map file", occured.getMessage());
    }
}
